/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package utilities;

import org.lwjgl.input.Keyboard;
import org.lwjgl.input.Mouse;
import org.lwjgl.opengl.Display;

/**
 *
 * @author ctuchsch
 */
public class BoardCamera extends CommonCamera
{

    private float pitchRates[];
    private float panRate;
    private float zoomRates[];
    private float zRates[];
    private int zoomInterval;
    private float maxPanX;
    private float maxPanY;

    public BoardCamera(Builder b)
    {
        super(b.aspectRatio, b.x, b.y, b.z, b.pitch, b.yaw, b.roll, b.zNear, b.zFar, b.fov);
        this.pitchRates = b.pitchRates;
        this.panRate = b.panRate;
        this.zoomRates = b.zoomRates;
        this.zRates = b.zRates;
        this.zoomInterval = b.startingStep - 1;
        this.maxPanX = b.maxPanX;
        this.maxPanY = b.maxPanY;
    }

    @Override
    public void processMouse()
    {
         float mouseDWheel = Mouse.getDWheel();
        //this doesn't work fix it
        if (mouseDWheel != 0)
            if (mouseDWheel > 0) {
                if (zoomInterval > 0) {
                    //move to next pos
                    zoomInterval--;
                    setY(zoomRates[zoomInterval]);
                    //setZ(z() - zRates[zoomInterval]);
                    setPitch(pitchRates[zoomInterval]);                    
                }
            }
            else if (mouseDWheel < 0)
                if (zoomInterval < zoomRates.length - 1) {
                    zoomInterval++;
                    setY(zoomRates[zoomInterval]);
                    //setZ(z() + zRates[zoomInterval]);
                    setPitch(pitchRates[zoomInterval]);                    
                }

        float mouseX = Mouse.getX();
        float mouseY = Display.getHeight() - Mouse.getY() - 1;

        //pan left
        if (mouseX <= 25 && x() > -maxPanX)
            setX(x() - panRate);
        if (mouseX >= (Display.getWidth() - 25) && x() < maxPanX)
            setX(x() + panRate);

        if (mouseY <= 25 && z() > -maxPanY)
            setZ(z() - panRate);
        if (mouseY >= (Display.getHeight() - 25) && z() < maxPanY)
            setZ(z() + panRate);
    }

    @Override
    public void processMouse(float mouseSpeed)
    {
        float mouseDWheel = Mouse.getDWheel();
        //this doesn't work fix it
        if (mouseDWheel != 0)
            if (mouseDWheel > 0) {
                if (zoomInterval > 0) {
                    //move to next pos
                    zoomInterval--;
                    setY(zoomRates[zoomInterval]);
                    //setZ(z() - zRates[zoomInterval]);
                    setPitch(pitchRates[zoomInterval]);                    
                }
            }
            else if (mouseDWheel < 0)
                if (zoomInterval < zoomRates.length - 1) {
                    zoomInterval++;
                    setY(zoomRates[zoomInterval]);
                    //setZ(z() + zRates[zoomInterval]);
                    setPitch(pitchRates[zoomInterval]);                    
                }

        float mouseX = Mouse.getX();
        float mouseY = Display.getHeight() - Mouse.getY() - 1;

        //pan left
        if (mouseX <= 25 && x() > -maxPanX)
            setX(x() + panRate);
        if (mouseX >= (Display.getWidth() - 25) && x() < maxPanX)
            setX(x() - panRate);

        if (mouseY <= 25 && z() > -maxPanY)
            setZ(z() - panRate);
        if (mouseY >= (Display.getHeight() - 25) && z() < maxPanY)
            setZ(z() + panRate);
    }

    @Override
    public void processMouse(float mouseSpeed, float maxLookUp, float maxLookDown)
    {
        float mouseDWheel = Mouse.getDWheel();
        //this doesn't work fix it
        if (mouseDWheel != 0)
            if (mouseDWheel > 0) {
                if (zoomInterval > 0) {
                    //move to next pos
                    zoomInterval--;
                    setY(zoomRates[zoomInterval]);
                    //setZ(z() - zRates[zoomInterval]);
                    setPitch(pitchRates[zoomInterval]);                    
                }
            }
            else if (mouseDWheel < 0)
                if (zoomInterval < zoomRates.length - 1) {
                    zoomInterval++;
                    setY(zoomRates[zoomInterval]);
                    //setZ(z() + zRates[zoomInterval]);
                    setPitch(pitchRates[zoomInterval]);                    
                }

        float mouseX = Mouse.getX();
        float mouseY = Display.getHeight() - Mouse.getY() - 1;

        //pan left
        if (mouseX <= 25 && x() < maxPanX)
            setX(x() + panRate * ((zoomInterval + 1) * .3f));
        if (mouseX >= (Display.getWidth() - 25) && x() > -maxPanX)
            setX(x() - panRate * ((zoomInterval + 1) * .3f));

        if (mouseY <= 25 && z() < maxPanY)
            setZ(z() + panRate * ((zoomInterval + 1) * .3f));
        if (mouseY >= (Display.getHeight() - 25) && z() > -maxPanY)
            setZ(z() - panRate * ((zoomInterval + 1) * .3f));
    }

    @Override
    public void processKeyboard(float delta)
    {
        throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
    }

    @Override
    public void processKeyboard(float delta, float speed)
    {
        //  throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
    }

    @Override
    public void processKeyboard(float delta, float speedX, float speedY, float speedZ)
    {
    }

    @Override
    public void moveFromLook(float dx, float dy, float dz)
    {
        // throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
    }

    public static class Builder
    {

        private float aspectRatio = 1;
        private float x = 0,
                y = 0,
                z = 0,
                pitch = 50,
                yaw = 0,
                roll = 0;
        private float zNear = 0.3f;
        private float zFar = 100;
        private float fov = 90;
        private float zoomRates[];
        private float pitchRates[];
        private float zRates[];
        private float panRate = .02f;
        private float maxPanX = 1;
        private float maxPanY = 1;
        private float minY = .1f;
        private float maxY = 3.5f;
        private float minPitch = 10;
        private float maxPitch = 80;
        private float minZ = 0;
        private float maxZ = .05f;
        private int zoomSteps = 20;
        private int startingStep = 10;

        public Builder()
        {
        }

        /**
         * Sets the aspect ratio of the camera.
         *
         * @param aspectRatio the aspect ratio of the camera (window width /
         * window height)
         * @return this
         */
        public Builder setAspectRatio(float aspectRatio)
        {
            if (aspectRatio <= 0)
                throw new IllegalArgumentException("aspectRatio " + aspectRatio + " was 0 or was smaller than 0");
            this.aspectRatio = aspectRatio;
            return this;
        }

        /**
         * Sets the distance from the camera to the near clipping pane.
         *
         * @param nearClippingPane the distance from the camera to the near
         * clipping pane
         * @return this
         * @throws IllegalArgumentException if nearClippingPane is 0 or less
         */
        public Builder setNearClippingPane(float nearClippingPane)
        {
            if (nearClippingPane <= 0)
                throw new IllegalArgumentException("nearClippingPane " + nearClippingPane + " is 0 or less");
            this.zNear = nearClippingPane;
            return this;
        }

        /**
         * Sets the distance from the camera to the far clipping pane.
         *
         * @param farClippingPane the distance from the camera to the far
         * clipping pane
         * @return this
         * @throws IllegalArgumentException if farClippingPane is 0 or less
         */
        public Builder setFarClippingPane(float farClippingPane)
        {
            if (farClippingPane <= 0)
                throw new IllegalArgumentException("farClippingPane " + farClippingPane + " is 0 or less");
            this.zFar = farClippingPane;
            return this;
        }

        /**
         * Sets the field of view angle in degrees in the y direction.
         *
         * @param fov the field of view angle in degrees in the y direction
         * @return this
         */
        public Builder setFieldOfView(float fov)
        {
            this.fov = fov;
            return this;
        }

        /**
         * Sets the position of the camera.
         *
         * @param x the x-coordinate of the camera
         * @param y the y-coordinate of the camera
         * @param z the z-coordinate of the camera
         * @return this
         */
        public Builder setPosition(float x, float y, float z)
        {
            this.x = x;
            this.y = y;
            this.z = z;
            return this;
        }

        /**
         * Sets the rotation of the camera.
         *
         * @param pitch the rotation around the x-axis in degrees
         * @param yaw the rotation around the y-axis in degrees
         * @param roll the rotation around the z-axis in degrees
         */
        public Builder setRotation(float pitch, float yaw, float roll)
        {
            this.pitch = pitch;
            this.yaw = yaw;
            this.roll = roll;
            return this;
        }

        public Builder setIntervals(float minY, float maxY, float minPitch, float maxPitch, int steps, int startingStep)
        {
            int startingIndex = startingStep - 1;

            if (startingIndex < 0 || startingIndex >= steps)
                throw new IllegalArgumentException("Starting step " + startingStep + " is out of bounds of the number of steps " + steps);
            if (steps < 0)
                throw new IllegalArgumentException("Number of steps " + steps + " is less than the min number of steps 0");

            this.minY = -minY;
            this.maxY = -maxY;
            this.minPitch = minPitch;
            this.maxPitch = maxPitch;
            this.zoomSteps = steps;
            this.startingStep = startingStep;
            return this;
        }

        public Builder setMaxPanPositions(float x, float y)
        {
            this.maxPanX = x;
            this.maxPanY = y;
            return this;
        }

        private void calculateSteps()
        {
            float zoomRates[] = new float[this.zoomSteps];
            float pitchRates[] = new float[this.zoomSteps];
            // float zRates[] = new float[this.zoomSteps];
            int startingIndex = startingStep - 1;

            float zoomInterval = (maxY - minY) / zoomSteps;
            float pitchInterval = (maxPitch - minPitch) / zoomSteps;
            //float zInterval = (maxZ - minZ) / zoomSteps;

            for (int i = 0; i < this.zoomSteps; i++) {
                zoomRates[i] = minY + (i * zoomInterval);
                pitchRates[i] = minPitch + (i * pitchInterval);
                //zRates[i] = minZ
            }
            this.zoomRates = zoomRates;
            this.pitchRates = pitchRates;
            this.pitch = pitchRates[startingIndex];
            this.y = zoomRates[startingIndex];

            for (float f : zoomRates)
                System.out.println(f);
            //this.zRates = zRates;
        }

        /**
         * Constructs an instance of EulerCamera from this builder helper class.
         *
         * @return an instance of EulerCamera
         * @throws IllegalArgumentException if farClippingPane is the same or
         * less than nearClippingPane
         */
        public BoardCamera build()
        {
            if (zFar <= zNear)
                throw new IllegalArgumentException("farClippingPane " + zFar + " is the same or less than nearClippingPane " + zNear);
            this.calculateSteps();
            return new BoardCamera(this);
        }
    }
}